#include "ChildVeinFinder.h"
#include "Line.h"
#include "Debug.h"

void ChildVeinFinder::Init(bool bOpposite, const VeinWalker::Criteria *pCritWalker,
                           const std::vector < ViewState > *pVecState1,
                           const std::vector < ViewState > *pVecState2, 
                           const JunctionFinder::Info *pJi )
{
	m_bOpposite = bOpposite;
	m_pVecState1 = pVecState1;
	m_pVecState2 = pVecState2;
	m_pJunctionInfo = pJi;
	m_nNodeIndex = 0;
	m_nRunIndex = -1;
	m_bLastRunWasBlockedBelow = false;
	m_bAt90 = false;
	m_critWalker = *pCritWalker;
	m_vecRuns.clear();
}

bool ChildVeinFinder::Next(Info *pInfo)
{
	const std::vector < JunctionFinder::Info::Node > *pVecJunction = m_bOpposite ? 
	      &m_pJunctionInfo->m_vecJunctionOppositeAngle : &m_pJunctionInfo->m_vecJunction;
/*	const std::vector < JunctionFinder::Info::Node > *pVecJunctionOpp = !m_bOpposite ? 
	      &m_pJunctionInfo->m_vecJunctionOppositeAngle : &m_pJunctionInfo->m_vecJunction;	
	const std::vector < JunctionFinder::Info::Node90 > *pVecJunction90 = m_bOpposite ?
	      &m_pJunctionInfo->m_vecJunction90OppositeAngle : &m_pJunctionInfo->m_vecJunction90;
	const std::vector < JunctionFinder::Info::Node90 > *pVecJunction90Opp = !m_bOpposite ?
	      &m_pJunctionInfo->m_vecJunction90OppositeAngle : &m_pJunctionInfo->m_vecJunction90;*/
	bool bPre1stRun;
	
	++m_nRunIndex;
	if (m_nRunIndex && m_nRunIndex == (S32)m_vecRuns.size()) {
		++m_nNodeIndex;
		m_vecRuns.clear();
	}
	if (m_bAt90)
		abort();
	while(1) {
		if (Invalid())
			return false; // the end, no more junction veins to explore
		
		if (bPre1stRun = !m_vecRuns.size()) {
			SetStateForNext();
			RunFinder<ViewBW> rf(ViewBW::GetThis());
			rf.GetAdjacentVertRuns(m_run = rf.GetVertRunForPoint(0, 0, 0xFF), 1, 0xFF, 
			                       &m_vecRuns);
			m_nMainVeinRun = GetRunClosestToMainVein();
			m_nRunIndex = 0;
			m_bLastRunWasBlockedBelow = false;
		}
		S32 nRuns = m_vecRuns.size();
		if (nRuns < 2) {
			Debug::DumpDebugInfo(this);
			abort();
		}
		const JunctionFinder::Info::Node *pNode = &(*pVecJunction)[m_nNodeIndex];
		while(m_nRunIndex < nRuns) {
			JunctionFinder::Info::Node::Blocked blk = pNode->m_vecBlocked[m_nRunIndex];
			if (bPre1stRun) {
				if (blk.FromAbove())
					abort(); 
				else if (blk.FromBelow()) {
					m_bLastRunWasBlockedBelow = true;
					bPre1stRun = false;
					if (m_nRunIndex != m_nMainVeinRun)
						break;
				}
			} else {
				if (m_bLastRunWasBlockedBelow) {
					if (blk.FromAbove()) {
						m_bLastRunWasBlockedBelow = blk.FromBelow();
						if (m_nRunIndex != m_nMainVeinRun)
							break;
					}
					else if (blk.FromBelow())
						abort();
				} else {
					if (blk.FromAbove())
						abort();
					else if (blk.FromBelow()) 
						m_bLastRunWasBlockedBelow = true;
				}
			}
			++m_nRunIndex;
		}
		if (m_nRunIndex == nRuns) {
			if (bPre1stRun) 
				abort();
			else {
				++m_nNodeIndex;
				m_vecRuns.clear();
				// after this, we start the loop again
			}
		} else {
			F64 nRow = m_vecRuns[m_nRunIndex].AvgPos(); 
			F64 nCol = 1.0;
			View::TransformPoints(&nRow, &nCol);
			VeinWalker vw(GetSourceImage(), nRow, nCol, View::GetCenterAngle(),
			              &m_critWalker);
			if (pInfo != NULL) {
				pInfo->m_stateFirst = vw.GetView()->GetViewState();
				vw.GetVeinInfo(true, &pInfo->m_infoVein);
				FakeConnection(&pInfo->m_stateFirst, &pInfo->m_infoVein.m_vecStates2);
			}
			break;
		} 
	}
	
	return true;
}

bool ChildVeinFinder::Invalid() const {
	const std::vector < JunctionFinder::Info::Node > *pVecJunction = m_bOpposite ? 
	      &m_pJunctionInfo->m_vecJunctionOppositeAngle : &m_pJunctionInfo->m_vecJunction;
	return m_nRunIndex == -2 || m_nNodeIndex == (S32)pVecJunction->size();
}

void ChildVeinFinder::SetStateForNext()
{
	const std::vector < JunctionFinder::Info::Node > *pVecJunction = m_bOpposite ? 
	      &m_pJunctionInfo->m_vecJunctionOppositeAngle : 
	      &m_pJunctionInfo->m_vecJunction;
	S32 nState = (*pVecJunction)[m_nNodeIndex].m_nViewStateIndex;
	SetStateForNextInOtherView(View::GetThis(), nState);
}

void ChildVeinFinder::SetStateForNextInOtherView(View *pView, S32 nState)
{
	if (nState < (S32)m_pVecState1->size()) {
		pView->SetViewState(&(*m_pVecState1)[nState]);
		if (m_bOpposite)
			pView->SetCenterAngle(GetCenterAngle() + 180.0);
	} else {
		pView->SetViewState(&(*m_pVecState2)[nState - m_pVecState1->size()]);
		if (!m_bOpposite)
			pView->SetCenterAngle(GetCenterAngle() + 180.0);	
	}	
}

S32 ChildVeinFinder::GetRunClosestToMainVein()
{
	const std::vector < JunctionFinder::Info::Node > *pVecJunction = m_bOpposite ? 
	      &m_pJunctionInfo->m_vecJunctionOppositeAngle : 
	      &m_pJunctionInfo->m_vecJunction;
	S32 nState = (*pVecJunction)[m_nNodeIndex].m_nViewStateIndex;
	if (nState == (S32)m_pVecState1->size() - 1) {
#ifdef DEBUG_BREAK_IF_GOT_JUNCTION_AT_END_OF_MAIN_VEIN
		Debug::Break();
#endif
		abort();
		return -1;
	}
	if (nState < 0 || 
	    nState >= (S32)m_pVecState1->size() + (S32)m_pVecState2->size() - 1) abort();
	S32 nNextState;
	if (nState < (S32)m_pVecState1->size()) 
		nNextState = nState + (m_bOpposite ? -1 : +1);
	else
		nNextState = nState + (m_bOpposite ? +1 : -1);
	View v(GetSourceImage(), 0, 0, 0);
	SetStateForNextInOtherView(&v, nNextState);
	S32 nRuns = m_vecRuns.size();
	F64 nMinSqrDist = INFINITY;
	S32 nClosestRun = -1;
	F64 nRow = v.GetCenterRow(), nCol = v.GetCenterCol(); 
	View::UntransformPoints(&nRow, &nCol);
	while(nRuns--) {
		F64 nSqrDist = Math::PointDist2(nRow - m_vecRuns[nRuns].AvgPos(), nCol - 1.0);
		if (nSqrDist < nMinSqrDist) {
			nMinSqrDist = nSqrDist;
			nClosestRun = nRuns;
		}
	}
	if (nClosestRun == -1) abort();
	return nClosestRun;
}

void ChildVeinFinder::FakeConnection(const ViewState *pSrcState, std::vector<ViewState> *pVec) const 
{
	Line<S32> line((S32)pSrcState->m_nCenterRow, (S32)pSrcState->m_nCenterCol, 
	               (S32)GetCenterRow(), (S32)GetCenterCol());
	Line<S32>::Iterator iter;
	pVec->clear();
	U32 nAngle = Math::AngleOfPoints(GetCenterCol() - pSrcState->m_nCenterCol,
	                                 GetCenterRow() - pSrcState->m_nCenterRow);
	for(line.Begin(&iter); !iter.End(); iter.Next()) {
		Point<S32> pt = iter.GetPoint();
		pVec->push_back(ViewState(pt.Row(), pt.Col(), nAngle));
	}
}
